package com.sunday.common.trace.skywalking.study.dubbo.provider;

import com.google.gson.JsonObject;
import com.sunday.common.core.remoting.base.page.PageResultDTO;
import com.sunday.common.core.remoting.base.response.Response;
import com.sunday.common.trace.skywalking.study.dubbo.dto.PageUserRoleReqDTO;
import com.sunday.common.trace.skywalking.study.dubbo.dto.PageUserRoleResDTO;
import com.sunday.common.trace.skywalking.study.dubbo.dto.SaveUserRoleReqDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.skywalking.apm.toolkit.trace.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Slf4j
@DubboService
@RequiredArgsConstructor
public class UserRoleProviderImpl implements UserRoleProvider {

    private final ThreadPoolTaskExecutor threadPool;

    @Override
    public Response save(SaveUserRoleReqDTO saveUserRoleReqDTO) {
        log.info("======= {}", saveUserRoleReqDTO);
        return Response.ok();
    }

    @Override
    public Response<PageResultDTO<PageUserRoleResDTO>> page(PageUserRoleReqDTO pageUserRoleReqDTO) {
        log.info("======= {}", pageUserRoleReqDTO);
        return Response.ok();
    }

    @Override
    public Response thread() {
        log.info("======= thread");

        threadPool.execute(RunnableWrapper.of(() -> log.info("{}", "[RunnableWrapper][execute] ============================")));

        threadPool.submit(CallableWrapper.of(() -> {
            log.info("{}", "[CallableWrapper][submit] ============================");
            return null;
        }));

        threadPool.execute(() -> log.info("{}", "[execute] **************************************"));

        threadPool.submit(() -> {
            log.info("{}", "[submit] **************************************");
            return null;
        });

        return Response.ok();
    }

    @Override
    public Response timeout(Long seconds) throws InterruptedException {
        if (seconds == null) {
            throw new RuntimeException("seconds 不能为空");
        }
        TimeUnit.SECONDS.sleep(seconds);
        return Response.ok();
    }

    /**
     * https://skywalking.apache.org/docs/skywalking-java/next/en/setup/service-agent/java-agent/application-toolkit-trace/
     */
    @Override
    @Trace(operationName = "TRACE_STAGE_A")
    @Tag(key = "tag1", value = "arg[0]")
    @Tag(key = "result", value = "returnedObj")
    public Response trace(String message) {
        log.info("[request data] {}", message);
        log.info("[traceId] {}", TraceContext.traceId());
        log.info("[segmentId] {}", TraceContext.segmentId());
        log.info("[spanId] {}", TraceContext.spanId());

        TraceContext.putCorrelation("name", "小明");
        log.info("user json : {}", findRoleById(1));

        log.info("user build : {}", build());

        return Response.ok();
    }

    @Trace(operationName = "FIND_ROLE_BY_ID_STAGE_B")
    @Tag(key = "tag1", value = "arg[0]")
    @Tag(key = "result", value = "returnedObj")
    private String findRoleById(int roleId) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("id", roleId);

        Optional<String> name = TraceContext.getCorrelation("name");
        jsonObject.addProperty("name", name.orElse(null));
        try {
            TimeUnit.MILLISECONDS.sleep(500L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return jsonObject.toString();
    }

    @Trace(operationName = "BUILD_STAGE_C")
    @Tag(key = "tag1", value = "arg[0]")
    @Tag(key = "username", value = "returnedObj.username")
    @Tag(key = "password", value = "returnedObj.password")
    @Tag(key = "time", value = "returnedObj.time")
    private User build() {

        /**
         * 与 @Trace 配合，代码级别填充
         * 添加到要跟踪的任何方法。之后，您可以在堆栈中看到跨度。@Trace
         * 注释的方法将尝试使用给定的键 （） 和 （） 标记当前活动范围 如果根本没有活动跨度，则此注释无效。 可以重复使用，并且可以与 一起使用，请参阅下面的示例。 的与自定义增强跟踪中支持的内容相同。@TagTag#key()Tag#value()@Tag@TracevalueTag
         * 在跟踪方法的上下文中添加自定义标记。ActiveSpan.tag("key", "val")
         * ActiveSpan.error()将当前范围标记为错误状态。
         * ActiveSpan.error(String errorMsg)使用消息将当前范围标记为错误状态。
         * ActiveSpan.error(Throwable throwable)使用可抛出对象将当前范围标记为错误状态。
         * ActiveSpan.debug(String debugMsg)在当前范围中添加调试级别日志消息。
         * ActiveSpan.info(String infoMsg)在当前范围中添加信息级别日志消息。
         * ActiveSpan.setOperationName(String operationName)自定义操作名称。
         */
        ActiveSpan.tag("my_tag", "my_value");
        ActiveSpan.error();
        ActiveSpan.error("Test-Error-Reason");

        ActiveSpan.error(new RuntimeException("Test-Error-Throwable"));
        ActiveSpan.info("Test-Info-Msg");
        ActiveSpan.debug("Test-debug-Msg");

        return new User("111", "222", System.currentTimeMillis());
    }

    record User(String username, String password, long time){}

}